package grame;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Resource（Wildcard）Permission
 *
 * @author "Shining Wang<shiningwang@topca.com>"
 */
@SuppressWarnings("unused")
public class ResourcePermission {
    private String resName;
    private Serializable resId;
    private String operationType;

    public String getResourceName() {
        return resName;
    }

    public Serializable getResourceId() {
        return resId;
    }

    public String getOperationType() {
        return operationType;
    }

    private static final Logger log = LoggerFactory.getLogger(ResourcePermission.class);

    protected static final String WILDCARD_TOKEN = "*";
    protected static final String PART_DIVIDER_TOKEN = ":";
    protected static final String SUBPART_DIVIDER_TOKEN = ",";
    protected static final boolean DEFAULT_CASE_SENSITIVE = false;

    public static final String PERMISSION_TARGET_RESOURCE = "resources";
    public static final String ANY_RESOURCE = WILDCARD_TOKEN;
    public static final Serializable ANY_RESOURCE_ID = WILDCARD_TOKEN;
    public static final String ANY_OPERATION = WILDCARD_TOKEN;
    public static final String OPERATION_CREATE = "c";
    public static final String OPERATION_RETRIEVE = "r";
    public static final String OPERATION_UPDATE = "u";
    public static final String OPERATION_DELETE = "d";

    /**
     * @param resName resource's name
     * @param resId   resource's id
     * @return "$PERMISSION_TARGET_RESOURCE:$resName:$resId:$ANY_OPERATION"
     */
    public static String toPermission(String resName, Serializable resId) {
        return toPermission(resName, resId, ANY_OPERATION);
    }

    /**
     * @param resName       resource's name
     * @param resId         resource's id
     * @param operationType one of OPERATION_CREATE, OPERATION_RETRIEVE, OPERATION_UPDATE and OPERATION_DELETE
     * @return "$PERMISSION_TARGET_RESOURCE:$resName:$resId:$operationType"
     */
    public static String toPermission(String resName, Serializable resId, String operationType) {
        if (resName == null || resName.isEmpty())
            resName = ANY_RESOURCE;
        if (resId == null || resId.toString().isEmpty())
            resId = ANY_RESOURCE_ID;
        if (operationType == null || operationType.isEmpty())
            operationType = ANY_OPERATION;

        log.trace("Create permission with resName:" + resName + "resId:" + resId + " operationType:" + operationType);
        String permission = PERMISSION_TARGET_RESOURCE + PART_DIVIDER_TOKEN + resName + PART_DIVIDER_TOKEN + resId + PART_DIVIDER_TOKEN + operationType;
        log.trace("wildcard resource permission result:" + permission);
        return permission;
    }

    public static String toPermission(String resName, Serializable resId, Collection<String> operationTypes) {
        String operationTypesStr = "";
        if (operationTypes != null) {
            for (String operationType : operationTypes) {
                if (!operationTypesStr.equals("")) {
                    operationTypesStr += SUBPART_DIVIDER_TOKEN;
                }
                operationTypesStr += operationType;
            }
        }
        return toPermission(resName, resId, operationTypesStr);
    }

    public static String toPermission(String resName, Collection<Serializable> resIds, Collection<String> operationTypes) {
        String resIdsStr = "";
        if (resIds != null) {
            for (Serializable resId : resIds) {
                if (!resIdsStr.equals("")) {
                    resIdsStr += SUBPART_DIVIDER_TOKEN;
                }
                resIdsStr += resId;
            }
        }
        String operationTypesStr = "";
        if (operationTypes != null) {
            for (String operationType : operationTypes) {
                if (!operationTypesStr.equals("")) {
                    operationTypesStr += SUBPART_DIVIDER_TOKEN;
                }
                operationTypesStr += operationType;
            }
        }
        return toPermission(resName, resIdsStr, operationTypesStr);
    }

    /**
     * @param permissions    the user permissions collection (source)
     * @param resName        resource's name
     * @param operationTypes the collection of operation type. default is [OPERATION_CREATE, OPERATION_RETRIEVE, OPERATION_UPDATE, OPERATION_DELETE]
     * @return the matches collection of resource's id. if any of the resource's id is the matches, return only one ANY_RESOURCE_ID in list
     */
    public static List<Serializable> findIdCollectionFromPermissionsByResourceNameAndOperationTypes(@NotNull Collection<String> permissions, @NotNull String resName, Collection<String> operationTypes) {
        if (operationTypes == null || operationTypes.isEmpty() || operationTypes.contains(ANY_OPERATION)) {
            operationTypes = new ArrayList<String>();
            operationTypes.add(OPERATION_CREATE);
            operationTypes.add(OPERATION_RETRIEVE);
            operationTypes.add(OPERATION_UPDATE);
            operationTypes.add(OPERATION_DELETE);
        }
        List<Serializable> idList = new ArrayList<Serializable>();
        for (String permission : permissions) {
            // resources:$resName:$resId:$operationTypes
            String[] parts = permission.split(PART_DIVIDER_TOKEN);
            // parts[1] is resName, parts[2] is resId, parts[3] is operationTypes
            // 如果权限中表明拥有所有resource权限，则直接返回仅含有ANY_RESOURCE_ID的结果集
            if (ANY_RESOURCE.equals(parts[1]) || ANY_RESOURCE_ID.equals(parts[2])) {
                idList.clear();
                idList.add(ANY_RESOURCE_ID);
                break;
            }
            checkOperationType:
            {
                switch (parts.length) {
                    case 4:
                        for (String operationType : operationTypes) {
                            // 拥有指定的所有权限，则继续执行，否则跳出checkOpertaionType
                            if (!ANY_OPERATION.equals(parts[3]) && !parts[3].contains(operationType))
                                break checkOperationType;
                        }
                        // parts[3]包含所有operationType
                        // 继续执行 idList.add(parts[2]);
                    case 3: // parts[3]未指定，即满足所有operationTypes
                        // 将当前id（parts[2]）加入到列表
                        idList.addAll(Arrays.asList(parts[2].split(SUBPART_DIVIDER_TOKEN)));
                }
            }
        }
        // 去除重复id
        List<Serializable> result = new ArrayList<Serializable>();
        for (Serializable id : idList) {
            if (!result.contains(id))
                result.add(id);
        }
        return result;
    }
}
